home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-02
/
storage.zip
/
STORAGE.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1991-02-09
|
18KB
|
615 lines
Unit Storage;
{ STORAGE.PAS - 13 Jan 91
This unit was created to replace the original system storage that was
created for the DMG. It is designed to be object oriented and will
also alow for external compression routines to be designed into the
system with a registration code for each.
The system will take a buffer pointer and run it through the compressor
until it reaches "BufBytes" number of characters in the buffer. Once the
compressor is finished, the resulting bitstream is then written to the
disk. An index number is returned for where this was written.
The system that reads the messages only needs an index and filename.
It will create a buffer for the message up to the memory restraints.
You MUST do a .done when you are through with the buffer or the space
will not be released to the heap.
__________________________________________________________________________
MODIFICATIONS:
09 Feb 91 - Removed the original compression routines (The old code is
still at the end of the listing it anyone cares) and replaced
them with a technique based on the SPLAY tree algorithms. The
original code for this came from the file SPLAY2.ZIP written by Kim
Kokkonen from TurboPower Software. Documentation on this compression
routine can be found from an article by Douglas W. Jones, "Application
of Splay Trees to Data Compression", in Communications of the ACM,
August 1988, page 996.
Other changes include creating a message header for each compressed
message with an overhead of (currently) nine bytes. Note that I am
reserving compression types 0..10 for myself and anyone can use the
others to their hears desires.
Also removed the internal disk buffers that I created... Forgot that if
your using TBufStream, its already buffered. Why waste the memory
buffering it twice?!?
NOTE: This version is no longer compatable with those published before it.
}
{$F+,O+,S-,R-}
Interface
Uses Dos, Objects, Memory;
CONST stStoreError = -120;
stStoreReadErr = 197;
stStoreWriteErr = 198;
stStoreUnknownErr = 199;
MemOverflow = 1005;
TYPE PBuffer = ^BBuffer;
BBuffer = ARRAY [0..65530] OF BYTE;
PList = ^LList;
LList = RECORD
OldItem : LONGINT;
NewItem : LONGINT;
Next : PList;
END;
PStorage = ^TStorage;
TStorage = OBJECT(TBufStream)
SFileName : FNameStr;
SCleanName : FNameStr;
SCleanIndex : PList;
SMode : WORD;
SHoldBuf : POINTER;
SHoldBufLen : WORD;
CONSTRUCTOR Init(AFileName : FNameStr; AMode, Size : WORD);
DESTRUCTOR Done; VIRTUAL;
FUNCTION WriteMsg(BufBytes : WORD; VAR Buf) : LONGINT;
FUNCTION ReadMsg(Index : LONGINT; VAR Buf : POINTER) : WORD;
PROCEDURE DeleteMsg(Index : LONGINT);
PROCEDURE CleanUpMsg;
FUNCTION NewIndex(Index : LONGINT) : LONGINT;
PROCEDURE DeleteCleanUp;
PROCEDURE InitCompress; VIRTUAL;
FUNCTION Compress(NumBytes : WORD; VAR CompType : BYTE;
VAR Buf) : WORD; VIRTUAL;
PROCEDURE DeCompress(NumBytes : WORD; CompType : BYTE; VAR Buf); VIRTUAL;
END;
Implementation
CONST MarkerWord = $114D4410; {Some sort of magic number!}
TYPE Header = RECORD
Marker : LONGINT;
ExpandSize : WORD;
CompressSize : WORD;
CompressType : BYTE
END;
VAR Head : Header;
{----------------------------------------------------------------------------}
CONSTRUCTOR TStorage.Init;
BEGIN
TBufStream.Init(AFileName,AMode,Size);
IF Status <> stOk THEN
Status := stStoreError
ELSE
BEGIN
SFileName := FEXPAND(AFileName);
SCleanName := '';
SCleanIndex := NIL;
SMode := AMode;
SHoldBuf := NIL;
SHoldBufLen := 0
END
END;
{----------------------------------------------------------------------------}
FUNCTION TStorage.WriteMsg;
VAR SIndex : LONGINT;
BEGIN
SIndex := GetSize;
WriteMsg := SIndex;
WITH Head DO BEGIN
Marker := MarkerWord;
ExpandSize := BufBytes;
CompressSize := 0;
CompressType := 0
END;
TBufStream.Seek(SIndex);
TBufStream.Write(Head,SIZEOF(Head));
Head.CompressSize := Compress(BufBytes,Head.CompressType,Buf);
TBufStream.Seek(SIndex);
TBufStream.Write(Head,SIZEOF(Head));
TBufStream.Flush;
IF Status <> stOk THEN
Status := stStoreError
END;
{----------------------------------------------------------------------------}
FUNCTION TStorage.ReadMsg;
VAR DeleteCheck : BYTE;
BEGIN
IF (SHoldBuf <> NIL) AND (SHoldBufLen > 0) THEN
FREEMEM(SHoldBuf,SHoldBufLen);
SHoldBuf := NIL;
SHoldBufLen := 0;
ReadMsg := 0;
TBufStream.Seek(Index);
TBufStream.Read(Head,SIZEOF(Head));
IF Head.Marker <> MarkerWord THEN
BEGIN
Head.ExpandSize := TBufStream.GetSize - Index;
IF Head.ExpandSize > 65530 THEN
Head.ExpandSize := 65530;
Head.CompressSize := Head.ExpandSize;
Head.CompressType := 0;
TBufStream.Seek(Index)
END
ELSE
IF Head.CompressType = $FF THEN
EXIT;
SHoldBuf := MemAlloc(Head.ExpandSize);
IF SHoldBuf <> NIL THEN
BEGIN
SHoldBufLen := Head.ExpandSize;
DeCompress(Head.CompressSize,Head.CompressType,SHoldBuf^);
ReadMsg := Head.ExpandSize
END
ELSE
Error(stStoreError,MemOverflow);
Buf := SHoldBuf;
IF Status <> stOk THEN
Status := stStoreError
END;
{----------------------------------------------------------------------------}
PROCEDURE TStorage.DeleteMsg;
VAR CompressType : BYTE;
BEGIN
Seek(Index);
Read(Head,SIZEOF(Head));
IF Head.Marker = MarkerWord THEN
BEGIN
Seek(Index);
Head.CompressType := $FF; {Mark Compression Type as Deleted!}
Write(Head,SIZEOF(Head))
END;
IF Status <> stOk THEN
Status := stStoreError
END;
{----------------------------------------------------------------------------}
PROCEDURE TStorage.CleanUpMsg;
VAR Dir : DirStr;
FName : NameStr;
Ext : ExtStr;
T : TBufStream;
TmpPtr : POINTER;
TFile : FILE;
OldItem : LONGINT;
NewItem : LONGINT;
LinkPtr : PList;
BEGIN
FSplit(SFileName,Dir,FName,Ext);
SCleanName := Dir + FName + '.$$$';
T.Init(SCleanName,stCreate,1024);
Seek(0);
OldItem := 0;
WHILE (OldItem < GetSize - 1) AND (Status = stOk) DO BEGIN
Read(Head,SIZEOF(Head));
IF Head.Marker <> MarkerWord THEN
Error(stStoreError,stStoreUnknownErr)
ELSE
BEGIN
TmpPtr := MemAlloc(Head.CompressSize);
IF TmpPtr = NIL THEN
Error(stStoreError,MemOverflow)
ELSE
BEGIN
Read(TmpPtr^,Head.CompressSize);
IF (Status = stOk) AND (Head.CompressType < $FF) THEN
BEGIN
NewItem := T.GetPos;
T.Write(Head,SIZEOF(Head));
T.Write(TmpPtr^,Head.CompressSize);
GETMEM(LinkPtr,SIZEOF(LList));
LinkPtr^.OldItem := OldItem;
LinkPtr^.NewItem := NewItem;
LinkPtr^.Next := SCleanIndex;
SCleanIndex := LinkPtr
END;
FREEMEM(TmpPtr,Head.CompressSize);
OldItem := GetPos
END
END
END;
T.Done;
IF Status <> stOk THEN
BEGIN
ASSIGN(TFile,SCleanName);
ERASE(TFile);
SCleanName := '';
Status := stStoreError
END
END;
{----------------------------------------------------------------------------}
FUNCTION TStorage.NewIndex;
VAR PLink : PList;
BEGIN
PLink := SCleanIndex;
NewIndex := -1;
WHILE (PLink <> NIL) AND (PLink^.OldItem <> Index) DO
PLink := PLink^.Next;
IF (PLink <> NIL) AND (PLink^.OldItem = Index) THEN
NewIndex := PLink^.NewItem
END;
{----------------------------------------------------------------------------}
PROCEDURE TStorage.DeleteCleanUp;
VAR TFile : FILE;
PLink : PList;
BEGIN
IF SCleanName <> '' THEN
BEGIN
{$I-} ASSIGN(TFile,SCleanName);
ERASE(TFile); {$I+}
ErrorInfo := IOResult;
IF ErrorInfo <> stOk THEN
Status := stStoreError;
SCleanName := '';
WHILE SCleanIndex <> NIL DO BEGIN
PLink := SCleanIndex;
SCleanIndex := PLink^.Next;
FREEMEM(PLink,SIZEOF(LList))
END
END
END;
{----------------------------------------------------------------------------}
CONST BitMask : ARRAY[0..7] OF BYTE = (1,2,4,8,16,32,64,128);
VAR Up : ARRAY[0..512] OF BYTE;
Left : ARRAY[0..255] OF WORD;
Right : ARRAY[0..255] OF WORD;
PROCEDURE Splay(Code : WORD); {Note 0..255 are characters, 256 is EOF}
VAR a : WORD;
b : WORD;
c : BYTE;
d : BYTE;
BEGIN
a := Code + 256;
REPEAT
c := Up[a];
IF c <> 0 THEN
BEGIN
d := Up[c];
b := Left[d];
IF c = b THEN
BEGIN
b := Right[d];
Right[d] := a
END
ELSE
Left[d] := a;
IF a = Left[c] THEN
Left[c] := b
ELSE
Right[c] := b;
Up[a] := d;
Up[b] := c;
a := d
END
ELSE
a := c
UNTIL a = 0
END;
{----------------------------------------------------------------------------}
FUNCTION TStorage.Compress;
VAR i : WORD;
NumWritten : WORD;
BitPos : BYTE;
OutByte : BYTE;
PROCEDURE WriteByte;
BEGIN
TBufStream.Write(OutByte,1);
INC(NumWritten);
BitPos := 0;
OutByte := 0
END;
PROCEDURE Comp(Code : WORD);
VAR a : WORD;
u : BYTE;
sp : WORD;
Stack : ARRAY[0..255] OF BOOLEAN;
BEGIN
a := Code + 256;
sp := 0;
REPEAT
u := Up[a];
Stack[sp] := (Right[u] = a);
INC(sp);
a := u
UNTIL a = 0;
REPEAT
DEC(sp);
IF Stack[sp] THEN
OutByte := OutByte OR BitMask[BitPos];
IF BitPos = 7 THEN
WriteByte
ELSE
INC(BitPos)
UNTIL sp = 0;
Splay(Code)
END;
BEGIN
InitCompress;
BitPos := 0;
OutByte := 0;
CompType := 2;
Compress := 0;
NumWritten := 0;
FOR i := 0 TO NumBytes - 1 DO
Comp(BBuffer(Buf)[i]);
Comp(256); {EOF Marker}
IF BitPos <> 0 THEN
WriteByte;
Compress := NumWritten
END;
{----------------------------------------------------------------------------}
PROCEDURE TStorage.DeCompress;
VAR NumWritten : WORD;
BufRead : WORD;
InByte : BYTE;
OutByte : WORD;
BitPos : BYTE;
FUNCTION Expand : WORD;
VAR a : WORD;
BEGIN
a := 0;
REPEAT
IF BitPos = 7 THEN
BEGIN
TBufStream.Read(InByte,1);
BitPos := 0
END
ELSE
INC(BitPos);
IF InByte AND BitMask[BitPos] = 0 THEN
a := Left[a]
ELSE
a := Right[a]
UNTIL a > 255;
DEC(a,256);
Splay(a);
Expand := a
END;
BEGIN
CASE CompType OF
0 : TBufStream.Read(Buf,NumBytes);
2 : BEGIN
InitCompress;
BitPos := 7;
BufRead := 0;
NumWritten := 0;
OutByte := Expand;
WHILE OutByte <> 256 DO BEGIN
BBuffer(Buf)[NumWritten] := OutByte;
INC(NumWritten);
OutByte := Expand
END
END
END
END;
{----------------------------------------------------------------------------}
PROCEDURE TStorage.InitCompress;
VAR i : WORD;
j : BYTE;
k : WORD;
BEGIN
FOR i := 1 TO 512 DO
Up[i] := (i - 1) SHR 1;
FOR j := 0 TO 255 DO BEGIN
k := (j + 1) SHL 1;
Left[j] := k - 1;
Right[j] := k
END
END;
{----------------------------------------------------------------------------}
DESTRUCTOR TStorage.Done;
VAR TFile : FILE;
PLink : PList;
BEGIN
IF (SHoldBuf <> NIL) AND (SHoldBufLen > 0) THEN
FREEMEM(SHoldBuf,SHoldBufLen);
TBufStream.Done;
IF SCleanName <> '' THEN
BEGIN
ASSIGN(TFile,SFileName);
ERASE(TFile);
ASSIGN(TFile,SCleanName);
RENAME(TFile,SFileName);
SCleanName := ''
END;
WHILE SCleanIndex <> NIL DO BEGIN
PLink := SCleanIndex;
SCleanIndex := PLink^.Next;
FREEMEM(PLink,SIZEOF(LList))
END
END;
{----------------------------------------------------------------------------}
END.
(*--------------------------------------------------------------------------*)
(*-- OLDER METHOD OF COMPRESSION/DECOMPRESSION --*
{----------------------------------------------------------------------------}
PROCEDURE TStorage.Compress;
VAR p : PBuffer;
ReadPosn : WORD;
WritePosn : WORD;
SpaceCount : WORD;
BEGIN
p := PBuffer(@Buf);
ReadPosn := 0;
WritePosn := 0;
WHILE (p^[ReadPosn] <> 0) AND (ReadPosn < 65530) DO BEGIN
SpaceCount := 0;
WHILE (p^[ReadPosn + SpaceCount] = 32) DO
INC(SpaceCount);
IF SpaceCount > 1 THEN
BEGIN
INC(ReadPosn,SpaceCount);
WHILE SpaceCount > 0 DO
IF SpaceCount > 255 THEN
BEGIN
p^[WritePosn] := 255;
p^[WritePosn + 1] := 255;
INC(WritePosn,2);
DEC(SpaceCount,255)
END
ELSE
BEGIN
p^[WritePosn] := 255;
p^[WritePosn + 1] := SpaceCount;
INC(WritePosn,2);
SpaceCount := 0
END;
SpaceCount := 2
END;
IF SpaceCount = 1 THEN
IF (p^[ReadPosn + 1] >= 64) AND (p^[ReadPosn + 1] <= 127) THEN
BEGIN
p^[WritePosn] := p^[ReadPosn + 1] + 128;
INC(WritePosn);
INC(ReadPosn,2)
END
ELSE
SpaceCount := 0;
IF SpaceCount = 0 THEN
BEGIN
IF p^[ReadPosn + 1] = 101 THEN
BEGIN
p^[WritePosn] := p^[ReadPosn] + 64;
INC(ReadPosn,2)
END
ELSE
BEGIN
p^[WritePosn] := p^[ReadPosn];
INC(ReadPosn)
END;
INC(WritePosn)
END
END;
p^[WritePosn] := 0;
MOVE(p^[0],p^[1],WritePosn + 1);
p^[0] := 1
END;
{----------------------------------------------------------------------------}
PROCEDURE TStorage.DeCompress;
VAR p : PBuffer;
ReadPosn : WORD;
Count : WORD;
Total : WORD;
BEGIN
p := PBuffer(@Buf);
ReadPosn := 0;
Total := 0;
WHILE (p^[Total + 1] <> 0) DO
INC(Total);
IF p^[0] = 1 THEN
BEGIN
MOVE(p^[1],p^[0],Total);
p^[Total] := 0;
WHILE (p^[ReadPosn] <> 0) AND (ReadPosn < SholdBufLen) DO BEGIN
CASE p^[ReadPosn] OF
255 : BEGIN
Count := p^[ReadPosn + 1];
MOVE(p^[ReadPosn + 2],p^[ReadPosn + Count],SHoldBufLen - ReadPosn - 2);
FILLCHAR(p^[ReadPosn],Count,32);
INC(ReadPosn,Count)
END;
192..254 : BEGIN
MOVE(p^[ReadPosn],p^[ReadPosn + 1],SHoldBufLen - ReadPosn);
p^[ReadPosn] := 32;
DEC(p^[ReadPosn + 1],128);
INC(ReadPosn,2)
END;
160..191 : BEGIN
MOVE(p^[ReadPosn],p^[ReadPosn + 1],SHoldBufLen - ReadPosn);
p^[ReadPosn + 1] := 101;
DEC(p^[ReadPosn],64);
INC(ReadPosn,2)
END;
000..159 : INC(ReadPosn)
END
END
END
END;
*--------------------------------------------------------------------------*)